{
  lib,
  stdenv,
  resholve,
  binlore,
  writeTextFile,
}:

rec {
  /*
    These functions break up the work of partially validating the
    'solutions' attrset and massaging it into env/cli args.

    Note: some of the left-most args do not *have* to be passed as
    deep as they are, but I've done so to provide more error context
  */

  # for brevity / line length
  spaces = l: builtins.concatStringsSep " " l;
  colons = l: builtins.concatStringsSep ":" l;
  semicolons = l: builtins.concatStringsSep ";" l;

  # Throw a fit with dotted attr path context
  nope = path: msg: throw "${builtins.concatStringsSep "." path}: ${msg}";

  # Special-case directive value representations by type
  phraseDirective =
    solution: env: name: val:
    if builtins.isInt val then
      toString val
    else if builtins.isString val then
      name
    else if true == val then
      name
    else if false == val then
      "" # omit!
    else if null == val then
      "" # omit!
    else if builtins.isList val then
      "${name}:${semicolons (map lib.escapeShellArg val)}"
    else
      nope [
        solution
        env
        name
      ] "unexpected type: ${builtins.typeOf val}";

  # Build fake/fix/keep directives from Nix types
  phraseDirectives =
    solution: env: val:
    lib.mapAttrsToList (phraseDirective solution env) val;

  # Custom ~search-path routine to handle relative path strings
  relSafeBinPath =
    input:
    if lib.isDerivation input then
      ((lib.getOutput "bin" input) + "/bin")
    else if builtins.isString input then
      input
    else
      throw "unexpected type for input: ${builtins.typeOf input}";

  # Special-case value representation by type/name
  phraseEnvVal =
    solution: env: val:
    if env == "inputs" then
      (colons (map relSafeBinPath val))
    else if builtins.isString val then
      val
    else if builtins.isList val then
      spaces val
    else if builtins.isAttrs val then
      spaces (phraseDirectives solution env val)
    else
      nope [
        solution
        env
      ] "unexpected type: ${builtins.typeOf val}";

  # Shell-format each env value
  shellEnv =
    solution: env: value:
    lib.escapeShellArg (phraseEnvVal solution env value);

  # Build a single ENV=val pair
  phraseEnv =
    solution: env: value:
    "RESHOLVE_${lib.toUpper env}=${shellEnv solution env value}";

  /*
    Discard attrs:
    - claimed by phraseArgs
    - only needed for binlore.collect
  */
  removeUnneededArgs =
    value:
    removeAttrs value [
      "scripts"
      "flags"
      "unresholved"
    ];

  # Verify required arguments are present
  validateSolution =
    {
      scripts,
      inputs,
      interpreter,
      ...
    }:
    true;

  # Pull out specific solution keys to build ENV=val pairs
  phraseEnvs =
    solution: value: spaces (lib.mapAttrsToList (phraseEnv solution) (removeUnneededArgs value));

  # Pull out specific solution keys to build CLI argstring
  phraseArgs =
    {
      flags ? [ ],
      scripts,
      ...
    }:
    spaces (flags ++ scripts);

  phraseBinloreArgs =
    value:
    let
      hasUnresholved = builtins.hasAttr "unresholved" value;
    in
    {
      drvs = value.inputs ++ lib.optionals hasUnresholved [ value.unresholved ];
      strip = if hasUnresholved then [ value.unresholved ] else [ ];
    };

  # Build a single resholve invocation
  phraseInvocation =
    solution: value:
    if validateSolution value then
      # we pass resholve a directory
      "RESHOLVE_LORE=${binlore.collect (phraseBinloreArgs value)} ${phraseEnvs solution value} ${resholve}/bin/resholve --overwrite ${phraseArgs value}"
    else
      throw "invalid solution"; # shouldn't trigger for now

  injectUnresholved =
    solutions: unresholved:
    (builtins.mapAttrs (name: value: value // { inherit unresholved; }) solutions);

  # Build resholve invocation for each solution.
  phraseCommands =
    solutions: unresholved:
    builtins.concatStringsSep "\n" (
      lib.mapAttrsToList phraseInvocation (injectUnresholved solutions unresholved)
    );

  /*
    subshell/PS4/set -x and : command to output resholve envs
    and invocation. Extra context makes it clearer what the
    Nix API is doing, makes nix-shell debugging easier, etc.
  */
  phraseContext =
    {
      invokable,
      prep ? ''cd "$out"'',
    }:
    ''
      (
        ${prep}
        PS4=$'\x1f'"\033[33m[resholve context]\033[0m "
        set -x
        : invoking resholve with PWD=$PWD
        ${invokable}
      )
    '';
  phraseContextForPWD =
    invokable:
    phraseContext {
      inherit invokable;
      prep = "";
    };
  phraseContextForOut = invokable: phraseContext { inherit invokable; };

  phraseSolution = name: solution: (phraseContextForOut (phraseInvocation name solution));
  phraseSolutions =
    solutions: unresholved: phraseContextForOut (phraseCommands solutions unresholved);

  writeScript =
    name: partialSolution: text:
    writeTextFile {
      inherit name text;
      executable = true;
      checkPhase = ''
        ${
          (phraseContextForPWD (
            phraseInvocation name (
              partialSolution
              // {
                scripts = [ "${placeholder "out"}" ];
              }
            )
          ))
        }
      ''
      + lib.optionalString (partialSolution.interpreter != "none") ''
        ${partialSolution.interpreter} -n $out
      '';
    };
  writeScriptBin =
    name: partialSolution: text:
    writeTextFile rec {
      inherit name text;
      executable = true;
      destination = "/bin/${name}";
      checkPhase = ''
        ${phraseContextForOut (
          phraseInvocation name (
            partialSolution
            // {
              scripts = [ "bin/${name}" ];
            }
          )
        )}
      ''
      + lib.optionalString (partialSolution.interpreter != "none") ''
        ${partialSolution.interpreter} -n $out/bin/${name}
      '';
    };
  mkDerivation =
    {
      pname,
      src,
      version,
      passthru ? { },
      solutions,
      ...
    }@attrs:
    let
      inherit stdenv;

      /*
        Knock out our special solutions arg, but otherwise
        just build what the caller is giving us. We'll
        actually resholve it separately below (after we
        generate binlore for it).
      */
      unresholved = (
        stdenv.mkDerivation (
          (removeAttrs attrs [ "solutions" ])
          // {
            inherit version src;
            pname = "${pname}-unresholved";
          }
        )
      );
    in
    /*
      resholve in a separate derivation; some concerns:
      - we aren't keeping many of the user's args, so they
        can't readily set LOGLEVEL and such...
      - not sure how this affects multiple outputs
    */
    lib.extendDerivation true passthru (
      stdenv.mkDerivation {
        src = unresholved;
        inherit version pname;
        buildInputs = [ resholve ];
        disallowedReferences = [ resholve ];

        # retain a reference to the base
        passthru = unresholved.passthru // {
          unresholved = unresholved;
          # fallback attr for update bot to query our src
          originalSrc = unresholved.src;
        };

        # do these imply that we should use NoCC or something?
        dontConfigure = true;
        dontBuild = true;

        installPhase = ''
          cp -R $src $out
        '';

        # enable below for verbose debug info if needed
        # supports default python.logging levels
        # LOGLEVEL="INFO";
        preFixup = phraseSolutions solutions unresholved;

        # don't break the metadata...
        meta = unresholved.meta;
      }
    );
}
